package org.icop.frame.eval;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.icop.model.ValuedCorrespondence;
import org.jbpt.graph.abs.IDirectedEdge;
import org.jbpt.graph.abs.IDirectedGraph;
import org.jbpt.hypergraph.abs.IVertex;


public class GraphEditEvaluator<G extends IDirectedGraph<E,V>, E extends IDirectedEdge<V>, V extends IVertex> implements Evaluator<G, E, V> {

	double weightSkippedVertex;
	double weightSubstitutedVertex;
	double weightGroupedVertex;
	double weightSkippedEdge;
	
	public class VPair {
		public V v1;
		public V v2;
		public VPair(V v1, V v2) { this.v1 = v1; this.v2 = v2; }
	}
	
	public GraphEditEvaluator(double weightSkippedVertex, double weightSubstitutedVertex, double weightGroupedVertex, double weightSkippedEdge){
		this.weightSkippedVertex = weightSkippedVertex;
		this.weightSubstitutedVertex = weightSubstitutedVertex;
		this.weightGroupedVertex = weightGroupedVertex;
		this.weightSkippedEdge = weightSkippedEdge;
	}
	
	@Override
	public double evaluate(G g1, G g2, Set<ValuedCorrespondence<V>> mapping) {
		
		int totalNrVertices = g1.getVertices().size() + g2.getVertices().size();
		int totalNrEdges = g1.getEdges().size() + g2.getEdges().size();

		Set<V> verticesMappedIn1 = new HashSet<>();
		Set<V> verticesMappedIn2 = new HashSet<>();
		Set<V> verticesGroupedIn1 = new HashSet<>();
		Set<V> verticesGroupedIn2 = new HashSet<>();

		//Relate each vertex to a group
		Map<V,Integer> vid1togid = new HashMap<V, Integer>();
		Map<V,Integer> vid2togid = new HashMap<V, Integer>();
		
		int gid = 1;
		double substitutionQuality = 0.0;
		for (ValuedCorrespondence<V> c : mapping) {
			verticesMappedIn1.addAll(c.getV1s());
			verticesMappedIn2.addAll(c.getV2s());

			for (V v1: c.getV1s())
				vid1togid.put(v1, gid);
			
			if (c.getV1s().size() > 1)
				verticesGroupedIn1.addAll(c.getV1s());

			for (V v2: c.getV2s())
				vid2togid.put(v2, gid);

			if (c.getV2s().size() > 1)
				verticesGroupedIn2.addAll(c.getV2s());

			substitutionQuality += c.getConfidenceValue();
			gid++;
		}
		
		int skippedVertices = totalNrVertices - verticesMappedIn1.size() - verticesMappedIn2.size();

		Set<E> groupedIn1 = new HashSet<E>();
		Set<E> groupedIn2 = new HashSet<E>();

		Set<E> mappedIn1 = new HashSet<E>();
		Set<E> mappedIn2 = new HashSet<E>();
		
		outer:
		for (E e1 : g1.getEdges()) {
			Integer gsrc = vid1togid.get(e1.getSource());
			Integer gtgt = vid1togid.get(e1.getTarget());
			if ((gsrc != null) && (gtgt != null)) {
				if (gsrc.equals(gtgt))
					groupedIn1.add(e1);
				else {
					Set<V> alignedToE1Source = new HashSet<>(); 
					Set<V> alignedToE1Target = new HashSet<>(); 
					for (ValuedCorrespondence<V> c : mapping) {
						if (c.getV1s().contains(e1.getSource()))
							alignedToE1Source.addAll(c.getV2s());
						if (c.getV1s().contains(e1.getTarget()))
							alignedToE1Target.addAll(c.getV2s());
					}
					for (E e2 : g2.getEdges()) {
						if (alignedToE1Source.contains(e2.getSource()) && alignedToE1Target.contains(e2.getTarget())) {
							mappedIn1.add(e1);
							break outer;
						}
					}
				}
			}
		}
		
		outer:
		for (E e2 : g2.getEdges()) {
			Integer gsrc = vid2togid.get(e2.getSource());
			Integer gtgt = vid2togid.get(e2.getTarget());
			if ((gsrc != null) && (gtgt != null)) {
				if (gsrc.equals(gtgt))
					groupedIn2.add(e2);
				else {
					Set<V> alignedToE2Source = new HashSet<>(); 
					Set<V> alignedToE2Target = new HashSet<>(); 
					for (ValuedCorrespondence<V> c : mapping) {
						if (c.getV2s().contains(e2.getSource()))
							alignedToE2Source.addAll(c.getV1s());
						if (c.getV2s().contains(e2.getTarget()))
							alignedToE2Target.addAll(c.getV1s());
					}
					for (E e1 : g1.getEdges()) {
						if (alignedToE2Source.contains(e1.getSource()) && alignedToE2Target.contains(e1.getTarget())) {
							mappedIn2.add(e2);
							break outer;
						}
					}
				}
			}
		}
		
		int mappedEdges = mappedIn1.size() +  mappedIn2.size();
		int groupedEdges = groupedIn1.size() +  groupedIn2.size();
		int skippedEdges = totalNrEdges - groupedEdges - mappedEdges;

		double vskip = skippedVertices / (1.0 * totalNrVertices);
		double vgroup = (1.0 * (verticesGroupedIn1.size() + verticesGroupedIn2.size())) / (1.0 * totalNrVertices);
		double vsubs = 1.0 - substitutionQuality / (1.0 * mapping.size());
		
		double gedSimilarity;
		if (totalNrEdges==0){
			gedSimilarity = ((weightSkippedVertex * vskip) + (weightGroupedVertex * vgroup) + (weightSubstitutedVertex * vsubs))/(weightSkippedVertex+weightSubstitutedVertex+weightGroupedVertex); 			
		}else{
			double eskip = (skippedEdges / (1.0 * totalNrEdges)); 
			gedSimilarity = ((weightSkippedVertex * vskip) + (weightGroupedVertex * vgroup)+ (weightSubstitutedVertex * vsubs) + (weightSkippedEdge * eskip))/(weightSkippedVertex+weightSubstitutedVertex+weightSkippedEdge+weightGroupedVertex); 			
		}
		return 1.0 - gedSimilarity;
	}
	
}
